package com.hay.pay.support.wechat;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.hay.pay.exception.PayException;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.*;

/**
 * @author LiMouRen
 * @date 2024/1/24
 */
@Data
@Slf4j
public class WechatPayRequest {

    /**
     * 正常响应码,204为退款响应码
     */
    private static final List<Integer> OK_CODE = Arrays.asList(200,204);

    private CloseableHttpClient httpClient;

    private HttpPost httpPost;

    private HttpGet httpGet;

    private String domain;

    private WechatPayConfig payConfig;

    public static WechatPayRequest initPost(){
        HttpPost httpPost = new HttpPost();
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        WechatPayRequest request = new WechatPayRequest();
        request.setHttpPost(httpPost);
        request.setDomain(WechatDomainEnum.CHINA.getDomain());
        return request;
    }

    public static WechatPayRequest initGet(){
        HttpGet httpGet = new HttpGet();
        httpGet.addHeader("Accept", "application/json");
        WechatPayRequest request = new WechatPayRequest();
        request.setHttpGet(httpGet);
        request.setDomain(WechatDomainEnum.CHINA.getDomain());
        return request;
    }

    private static JSONObject getResponseBody(CloseableHttpResponse response){
        int statusCode = response.getStatusLine().getStatusCode();
        String responseBodyStr = null;
        try {
            HttpEntity entity = response.getEntity();
            responseBodyStr = Objects.isNull(entity) ? "{}" : EntityUtils.toString(response.getEntity());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        JSONObject responseBody = JSONObject.parseObject(responseBodyStr);
        if (!OK_CODE.contains(statusCode)){
            String errorMessage = responseBody.getString("message");
            throw new PayException(errorMessage);
        }
        return responseBody;
    }

    public WechatPayRequest payConfig(WechatPayConfig payConfig){
        this.payConfig = payConfig;
        return this;
    }

    public WechatPayRequest domain(String domain){
        this.domain = domain;
        return this;
    }

    public WechatPayRequest header(String key,String value){
        if (Objects.nonNull(httpPost)){
            httpPost.addHeader(key,value);
        }else {
            httpGet.addHeader(key,value);
        }
        return this;
    }

    public WechatPayRequest build(){
        if (Objects.isNull(this.payConfig)){
            throw new PayException("微信支付配置不能为空");
        }
        httpClient = getHttpClient(this.payConfig.getMchId(),
                this.payConfig.getMchCertSerialNo(),
                this.payConfig.getMchCertPrivateKey(),
                this.payConfig.getPayCert());
        return this;
    }

    private CloseableHttpClient getHttpClient(String mchId, String serialNo, String privateKey, String payCertificate) {
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKey);
        X509Certificate wechatPayCert = PemUtil.loadCertificate(
                new ByteArrayInputStream(payCertificate.getBytes(StandardCharsets.UTF_8)));
        ArrayList<X509Certificate> wechatPayCertificates = new ArrayList<>();
        wechatPayCertificates.add(wechatPayCert);
        return WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, serialNo, merchantPrivateKey)
                .withWechatPay(wechatPayCertificates)
                .build();
    }


    public WechatPayRequest addHeader(String name,String value){
        httpPost.addHeader(name,value);
        return this;
    }

    public JSONObject execute(String url,ObjectNode rootNode) {
        return Objects.nonNull(httpPost) ? executePost(url,rootNode) : executeGet(url,rootNode);
    }

    /**
     * 执行请求
     */
    private JSONObject executePost(String url,ObjectNode rootNode) {
        String finalUrl = domain + url;
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            objectMapper.writeValue(bos, rootNode);
            httpPost.setURI(URI.create(finalUrl));
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            return getResponseBody(response);
        }catch (Exception e){
            log.error("finalUrl: {},params:{}, error: {}",finalUrl,rootNode,e.getMessage(),e);
            throw new PayException(e.getMessage());
        }
    }

    /**
     * 执行请求
     */
    private JSONObject executeGet(String url,ObjectNode rootNode) {
        String finalUrl = domain + url;
        try {
            URIBuilder uriBuilder = new URIBuilder(finalUrl);
            if (Objects.nonNull(rootNode)){
                Iterator<String> iterator = rootNode.fieldNames();
                while (iterator.hasNext()){
                    String next = iterator.next();
                    uriBuilder.addParameter(next, String.valueOf(rootNode.get(next)));
                }
            }
            httpGet.setURI(uriBuilder.build());
            CloseableHttpResponse response = httpClient.execute(httpGet);
            return getResponseBody(response);
        }catch (Exception e){
            log.error("finalUrl: {} , params:{}, error: {}",finalUrl,rootNode,e.getMessage(),e);
            throw new PayException(e.getMessage());
        }
    }

    /**
     * 回调响应
     * @param response 响应
     * @param notifySuccess 是否回调成功
     */
    public static void noticeResponse(HttpServletResponse response, boolean notifySuccess){
        try{
            int status = notifySuccess ? 200 : 500;
            String code = notifySuccess ? "SUCCESS" : "FAIL";
            response.setStatus(status);
            Map<String, String> map = new HashMap<>(2);
            map.put("code", code);
            map.put("message", code);
            response.setHeader("Content-type", ContentType.APPLICATION_JSON.getMimeType());
            response.getOutputStream().write(JSONObject.toJSONString(map).getBytes(StandardCharsets.UTF_8));
            response.flushBuffer();
        }catch (Exception e){
            log.error("noticeResponse error: {}",e.getMessage(),e);
            throw new PayException(e.getMessage());
        }
    }

}
